import { BaseCamera } from "./BaseCamera";
import { ShaderBase } from "./ShaderBase";
import { ShaderTool } from "./ShaderTool";
import { Vector3 } from "./math/Vector3";

export class ShaderMgr {
    

    static gl: WebGL2RenderingContext | WebGLRenderingContext;
    static camera: BaseCamera;
    static shaders: ShaderBase[] = [];
    static firstRender: boolean = false;
    static canvas: HTMLCanvasElement;


    static commonBufferFunc:Function;
    
    
    
    static regShader<T>(cls: (new () => T)): T {
        var ishader: any = new cls();
        ishader.gl = this.gl;
        if(this.commonBufferFunc){
            ishader.initBuffer = this.commonBufferFunc.bind(ishader);
        }
        ishader.init(this.gl);
        var tool: ShaderTool = new ShaderTool();
        tool.init(ishader);
        if((<ShaderBase>ishader).isCommonBuffer){
            this.commonBufferFunc = (<ShaderBase>ishader).initBuffer;
        }
        if(!(ishader instanceof BaseCamera)){
            this.shaders.push(ishader);
        }
        return ishader;
    }

    static render() {
        let camera = this.camera;
        
        if (camera && camera.needUpdateRender) {
            let gl = this.gl;
            this.clear();
            camera.update();
            this.shaders.forEach(shader => {
                let program = this.resetProgram(gl, shader);
                this.updateShader(camera, shader, gl,program);
            })
        }
        if(! camera && !this.firstRender){
            this.clear();
            let gl = this.gl;
            this.firstRender = true;
            this.shaders.forEach(shader => {
                let program = this.resetProgram(gl, shader);
                this.updateShader(camera, shader, gl,program);
            })
        }
    }

    static resetProgram(gl: WebGL2RenderingContext | WebGLRenderingContext,sharder:ShaderBase): WebGLProgram{
        //@ts-ignore
        let program = gl.program || sharder.program;
        if(program){
            gl.useProgram(program);
        }
        return program;
    }

    private static commonModuleMatrix:Matrix4[] = [];
    static updateShader(camera: BaseCamera, sharder: ShaderBase, gl: WebGL2RenderingContext | WebGLRenderingContext,program:WebGLProgram) {
        if(sharder.id >= 0){
            if(this.commonModuleMatrix[sharder.id]){
                sharder.moduleMatrix.set(this.commonModuleMatrix[sharder.id]);
            }else{

                this.commonModuleMatrix[sharder.id] = sharder.moduleMatrix;
            }
        }
        if (program && !sharder.u_MvpMatrix) {
            sharder.u_MvpMatrix = gl.getUniformLocation(program, "u_MvpMatrix");
        }
        
        if (sharder.u_MvpMatrix && camera)
            gl.uniformMatrix4fv(sharder.u_MvpMatrix, false, camera.getMvpMatrix(sharder.moduleMatrix, sharder.mvpMatrix).elements);
        sharder.draw(gl);
    }

    
    static clearColor:Vector3 = new Vector3(0.0, 0.0, 0.0)
    static clear(){
        let gl = this.gl;
        gl.clearColor(this.clearColor.x,this.clearColor.y,this.clearColor.z, 1.0);
        gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
        gl.polygonOffset(1.0, 1.0);
    }
}